/**
 * Copyright (C) 2009 Matteo Merli <mm@merlimat.org>
 * 
 * $Id: PxlSet.java 17 2009-01-12 12:36:59Z matteo.merli $
 * $URL: http://pxl-lang.googlecode.com/svn/trunk/src/main/java/pxl/types/PxlSet.java $
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *         http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package pxl.types;

import java.util.Collection;
import java.util.Map;
import java.util.Set;

import pxl.errors.TypeError;
import pxl.types.annotations.Exported;

import com.google.common.base.Join;
import com.google.common.collect.Sets;

public class PxlSet extends PxlObject
{

    private final Set<PxlObject> entries;

    public PxlSet()
    {
        entries = Sets.newHashSet();
    }

    public PxlSet( int size )
    {
        entries = Sets.newHashSetWithExpectedSize( size );
    }

    public PxlSet( Set<PxlObject> entries )
    {
        this.entries = Sets.newHashSet( entries );
    }

    @Override
    public String getType()
    {
        return "set";
    }

    @Override
    public String toString()
    {
        if ( entries.isEmpty() )
            return "set()";

        StringBuilder sb = new StringBuilder();
        sb.append( '{' );
        Join.join( sb, ", ", entries );
        sb.append( '}' );

        return sb.toString();
    }

    // // Operators

    @Exported
    @Override
    public PxlString __repr__()
    {
        return new PxlString( toString() );
    }

    @Exported
    @Override
    public PxlString __str__()
    {
        return __repr__();
    }

    @Exported
    @Override
    public PxlBoolean __bool__()
    {
        return PxlBoolean.valueOf( !entries.isEmpty() );
    }

    @Exported
    @Override
    public PxlIterator __getiterator__()
    {
        return new PxlBaseIterator( entries.iterator() );
    }

    @Exported
    public void add( PxlObject entry )
    {
        entries.add( entry );
    }

    @Exported
    @Override
    public PxlBoolean __contains__( PxlObject obj )
    {
        return PxlBoolean.valueOf( entries.contains( obj ) );
    }

    @Exported
    @Override
    public PxlInteger __len__()
    {
        return new PxlInteger( entries.size() );
    }

    @Exported
    public PxlBoolean isdisjoint( PxlObject other )
    {
        if ( !(other instanceof PxlSet) )
        {
            throw new TypeError( "Object is not iterable" );
        }

        PxlSet otherSet = (PxlSet) other;

        for ( PxlObject entry : entries )
            if ( otherSet.entries.contains( entry ) )
                return PxlBoolean.False;

        return PxlBoolean.True;
    }

    @Exported
    public PxlBoolean issubset( PxlObject other )
    {
        if ( !(other instanceof PxlSet) )
        {
            throw new TypeError( "Object is not iterable" );
        }

        PxlSet otherSet = (PxlSet) other;
        return PxlBoolean.valueOf( otherSet.entries.containsAll( entries ) );
    }

    @Exported
    @Override
    public PxlBoolean __lt__( PxlObject other )
    {
        return PxlBoolean.valueOf( issubset( other ).getValue()
                && __eq__( other ) == PxlBoolean.False );
    }

    @Exported
    @Override
    public PxlBoolean __lte__( PxlObject other )
    {
        return issubset( other );
    }
    
    @Exported
    public PxlBoolean issuperset( PxlObject other )
    {
        if ( !(other instanceof PxlSet) )
        {
            throw new TypeError( "Object is not iterable" );
        }

        PxlSet otherSet = (PxlSet) other;
        return PxlBoolean.valueOf( entries.containsAll( otherSet.entries ) );
    }

    @Exported
    @Override
    public PxlBoolean __gt__( PxlObject other )
    {
        return PxlBoolean.valueOf( issuperset( other ).getValue()
                && __eq__( other ) == PxlBoolean.False );
    }

    @Exported
    @Override
    public PxlBoolean __gte__( PxlObject other )
    {
        return issuperset( other );
    }

    @Exported
    @Override
    public PxlBoolean __eq__( PxlObject other )
    {
        if ( !(other instanceof PxlSet) )
        {
            return PxlBoolean.False;
        }

        PxlSet otherSet = (PxlSet) other;
        return PxlBoolean.valueOf( otherSet.entries.equals( entries ) );
    }

    @Exported
    @Override
    public PxlBoolean __neq__( PxlObject other )
    {
        return PxlBoolean.not( __eq__( other ) );
    }
    
    @Exported 
    public PxlSet union( PxlObject other )
    {
        if ( !(other instanceof PxlSet) )
        {
            throw new TypeError( "Object is not iterable" );
        }
        PxlSet otherSet = (PxlSet) other;
        
        PxlSet result = new PxlSet();
        result.entries.addAll( entries );
        result.entries.addAll( otherSet.entries );
        return result;
    }
    
    @Exported
    @Override
    public PxlObject __ior__( PxlObject other )
    {
        return union( other );
    }
    
    @Exported 
    public PxlSet intersection( PxlObject other )
    {
        if ( !(other instanceof PxlSet) )
        {
            throw new TypeError( "Object is not iterable" );
        }
        PxlSet otherSet = (PxlSet) other;
        
        PxlSet result = new PxlSet();
        result.entries.addAll( entries );
        result.entries.retainAll( otherSet.entries );
        return result;
    }
    
    @Exported
    @Override
    public PxlObject __iand__( PxlObject other )
    {
        return intersection( other );
    }
    
    @Exported 
    public PxlSet difference( PxlObject other )
    {
        if ( !(other instanceof PxlSet) )
        {
            throw new TypeError( "Object is not iterable" );
        }
        PxlSet otherSet = (PxlSet) other;
        
        PxlSet result = new PxlSet();
        for ( PxlObject entry : entries )
            if ( ! otherSet.entries.contains( entry ) )
                result.entries.add( entry );
        return result;
    }
    
    @Exported
    @Override
    public PxlObject __sub__( PxlObject other )
    {
        return difference( other );
    }
    
    @Exported 
    public PxlSet symmetric_difference( PxlObject other )
    {
        if ( !(other instanceof PxlSet) )
        {
            throw new TypeError( "Object is not iterable" );
        }
        PxlSet otherSet = (PxlSet) other;
        
        PxlSet result = new PxlSet();
        for ( PxlObject entry : entries )
            if ( ! otherSet.entries.contains( entry ) )
                result.entries.add( entry );
        for ( PxlObject entry : otherSet.entries )
            if ( ! entries.contains( entry ) )
                result.entries.add( entry );
        return result;
    }
    
    @Exported
    @Override
    public PxlObject __xor__( PxlObject other )
    {
        return symmetric_difference( other );
    }

    // Attributes

    private static final Map<String, PxlObject> attrs = PxlObject
            .getExportedMethods( PxlSet.class );

    @Override
    protected PxlObject getAttribute( String name )
    {
        return attrs.get( name );
    }

    @Override
    protected Collection<String> getAttributeNames()
    {
        return attrs.keySet();
    }

}
